<?php
/**
 * PHP function
 */
class My_Code_Function
{
    /**
     * PHP Code
     * @var string
     */
    protected $code;

    /**
     * List of funtion paramenters
     * @var array
     */
    protected $parameters = array();

    /**
     * Function scope
     * @var string 
     */
    protected $visibility = '';

    /**
     * Function's name
     * @var string
     */
    protected $name;

    /**
     * Is the function startix?
     * @var boolean
     */
    protected $isStatic = false;

    /**
     * Create function's comments?
     * @var boolean
     */
    protected $createComents = false;

    /**
     * Function's description
     * @var string
     */
    protected $description = '';

    /**
     * Data type returned by the funtion
     * @var string
     */
    protected $returnType = null;

    /**
     * Array of My_Code_Line 's or My_Code_Block 's
     * @var array
     */
    protected $lines = array();

    private $validVisibility = array('public', 'protected', 'pivate');

    /**
     * @see Factory
     */
    protected function __construct()
    {
        
    }

    /**
     * @return My_Code_Function
     */
    public static function factory()
    {
        return new self();
    }

    /**
     * Add a paremeter to the function
     * @param string $name
     * @param mixed $defaulValue
     * @return My_Code_Function
     */
    public function addParameter(My_Code_Function_Parameter $parameter)
    {
        $this->parameters[] = $parameter;
        return $this;
    }

    /**
     * Is the function static?
     * @return My_Code_Function
     */
    public function isStatic()
    {
        $this->isStatic = true;
        return $this;
    }

    /**
     * Set the scope of the function (public | protected | private)
     * @param string $visibility
     * @return My_Code_Function
     */
    public function setVisibility($visibility)
    {
        $visibility = strtolower($visibility);
        if (!in_array($visibility, $this->validVisibility))
        {
            $visibility = '';
        }
        $this->visibility = $visibility;
        return $this;
    }

    /**
     * @param string $name
     * @return My_Code_Function
     */
    public function setName($name)
    {
        $this->name = $name;
        return $this;
    }

    /**
     * Set the return data type
     * @param string $returnType
     * @return My_Code_Function
     */
    public function setReturnType($returnType = 'mixed')
    {
        $this->returnType = $returnType;
        return $this;
    }


    /**
     * Add a PHP code line the function's body
     * @param My_Code_Line $line
     * @return My_Code_Function
     */
    public function addCodeLine(My_Code_Line $line)
    {
        $this->lines[] = $line;
        return $this;
    }

    /**
     * Add a PHP code block to the function's body
     * @param My_Code_Block $block
     * @return My_Code_Function
     */
    public function addCodeBlock(My_Code_Block $block)
    {
        $this->lines[] = $block;
        return $this;
    }

    /**
     * @param boolean $string
     * @return mixed
     */
    protected function getParameters($string = false)
    {
        $parameters = $this->parameters;
        if (true == $string)
        {
            $parameters = '';
            if (is_array($this->parameters) && sizeof($this->parameters)>0)
            {
                $auxParameters = array();
                foreach ($this->parameters as $parameter)
                {
                    $name = $parameter->getName();
                    $value = $parameter->getValue();
           
                    $auxParameterString = '$'.$name;
                    if (My_Code_Function_Parameter::NO_DEFAULT_VALUE !== $value)
                    {
                        if (is_string($value)) $paramValue = '\''.$value.'\'';
                        elseif (is_numeric ($value)) $paramValue = $value;
                        elseif (is_null ($value)) $paramValue = 'null';
                        elseif (is_bool ($value) && true == $value) $paramValue = 'true';
                        elseif (is_bool ($value) && false == $value) $paramValue = 'false';
                        else $paramValue = $value;                        
                        $auxParameterString .= ' = '.$paramValue;
                    }
                    $auxParameters[] = $auxParameterString;
                }
                $parameters = implode(', ', $auxParameters);
            }
        }
        return $parameters;
    }

    /**
     * @param boolean $string
     * @return mixed
     */
    protected function getInnerCode($string = false)
    {
        $code = $this->lines;
        if (true == $string)
        {
            $code = '';
            if (is_array($this->lines) && sizeof($this->lines) > 0)
            {
                foreach ($this->lines as $line)
                {
                    $code .= $line->toString();
                }
            }
        }
        return $code;
    }

    /**
     * @return string
     */
    public function toString()
    {
        $code = sprintf("%s\n%s %s function %s (%s)\n{\n%s}",
                        (true == $this->createComents) ? $this->getComments() : '',
                        $this->visibility,
                        (true == $this->isStatic) ? 'static' : '',
                        $this->name,
                        $this->getParameters(true),
                        $this->getInnerCode(true));
        return $code;
    }

    /**
     * @return string
     */
    protected function getComments()
    {
        $comments = "\n/**";

        // Description
        if (!is_null($this->description))
        {
            $comments .= sprintf("\n* %s", $this->description);
        }

        // Commnent parameters
        if (is_array($this->parameters) && sizeof($this->parameters)>0)
        {
            foreach ($this->parameters as $parameter)
            {
                $comments .= sprintf("\n* %s", $parameter->getDocLine());
            }
        }
        
        // Comment return
        if (!is_null($this->returnType))
        {
            $comments .= sprintf("\n* @return %s", $this->returnType);
        }
        $comments .= "\n*/";
        return $comments;
    }



    /**
     * @param boolean $create
     * @param string $description
     * @return My_Code_Function
     */
    public function createComments($create = true, $description = 'CAUTION!! Auto-generated function')
    {
        $this->description = $description;
        $this->createComents = $create;
        return $this;
    }
}